package fct.mpc.snipehunter;

import java.util.Random;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import fct.mpc.utils.BluetoothService;
import fct.mpc.utils.Snipe;
import fct.mpc.utils.SnipeCollection;

public class SnipeHunterBattleActivity extends Activity {

	// Debugging
	private static final String TAG = "BattleActivity";
	private static final boolean D = true;

	// Message types sent from the BluetoothService Handler
	public static final int MESSAGE_STATE_CHANGE = 1;
	public static final int MESSAGE_READ = 2;
	public static final int MESSAGE_WRITE = 3;
	public static final int MESSAGE_DEVICE_NAME = 4;
	public static final int MESSAGE_TOAST = 5;
	public static final int MESSAGE_SERVER = 6;

	// Key names received from the BluetoothService Handler
	public static final String DEVICE_NAME = "device_name";
	public static final String TOAST = "toast";

	// Intent request codes
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;

	// Name of the connected device
	private String mConnectedDeviceName = null;
	// Array adapter for the conversation thread
	private ArrayAdapter<String> mAvailableSnipeArrayAdapter;
	// Local Bluetooth adapter
	private BluetoothAdapter mBluetoothAdapter = null;
	// Member object for the battle manager
	private BluetoothService mBluetoothService = null;

	private Snipe snipeSelf = null;
	private Snipe snipeOpponent = null;
	private int server;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (D)
			Log.e(TAG, "+++ ON CREATE +++");

		server = -1;

		// Set up the window layout
		setContentView(R.layout.battle);

		// Get local Bluetooth adapter
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

		// If the adapter is null, then Bluetooth is not supported
		if (mBluetoothAdapter == null) {
			Toast.makeText(this, "Bluetooth is not available",
					Toast.LENGTH_LONG).show();
			finish();
			return;
		}
		
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	}

	@Override
	public void onStart() {
		super.onStart();
		if (D)
			Log.e(TAG, "++ ON START ++");

		// If BT is not on, request that it be enabled.
		// setupBattle() will then be called during onActivityResult
		if (!mBluetoothAdapter.isEnabled()) {
			Intent enableIntent = new Intent(
					BluetoothAdapter.ACTION_REQUEST_ENABLE);
			startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
			// Otherwise, setup the battle
		} else {
			if (mBluetoothService == null)
				setupBattle();
		}
	}

	@Override
	public synchronized void onResume() {
		super.onResume();
		if (D)
			Log.e(TAG, "+ ON RESUME +");

		// Performing this check in onResume() covers the case in which BT was
		// not enabled during onStart(), so we were paused to enable it...
		// onResume() will be called when ACTION_REQUEST_ENABLE activity
		// returns.
		if (mBluetoothService != null) {
			// Only if the state is STATE_NONE, do we know that we haven't
			// started already
			if (mBluetoothService.getState() == BluetoothService.STATE_NONE) {
				// Start the Bluetooth service manager
				mBluetoothService.start();
			}
		}
	}

	@Override
	public synchronized void onPause() {
		super.onPause();
		if (D)
			Log.e(TAG, "- ON PAUSE -");
	}

	@Override
	public void onStop() {
		super.onStop();
		if (D)
			Log.e(TAG, "-- ON STOP --");
	}

	@Override
	public void onDestroy()
	{
		getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		
		super.onDestroy();
		
		// Stop the Bluetooth services
		if (mBluetoothService != null)
			mBluetoothService.stop();
		if (D)
			Log.e(TAG, "--- ON DESTROY ---");
	}

	private void ensureDiscoverable() {
		if (D)
			Log.d(TAG, "ensure discoverable");
		if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
			Intent discoverableIntent = new Intent(
					BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
			discoverableIntent.putExtra(
					BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
			startActivity(discoverableIntent);
		}
	}

	private void setupBattle() {
		Log.d(TAG, "setupBattle()");

		// Initialize the array adapter for the snipe list
		mAvailableSnipeArrayAdapter = new ArrayAdapter<String>(this,
				R.layout.battle_snipe_list);

		ListView mSnipeListView = (ListView) findViewById(R.id.battle_available_snipes);
		for (Snipe s : SnipeCollection.getAvailableSnipes())
			mAvailableSnipeArrayAdapter.add(s.getName());

		mSnipeListView.setAdapter(mAvailableSnipeArrayAdapter);

		mSnipeListView.setOnItemClickListener(new OnItemClickListener() {

			public void onItemClick(AdapterView<?> adapter, View view,
					int position, long id) {
				if (D)
					Log.d(TAG, adapter.getItemAtPosition(position).toString()
							+ ": Battle Start!!!");
				// Start battle with the selected Snipe
				exchangeSnipes(adapter.getItemAtPosition(position).toString());
			}

		});

		// Initialize the BluetoothService to perform bluetooth connections
		mBluetoothService = new BluetoothService(this, mHandler);
	}

	private void exchangeSnipes(String snipe) {

		// Check that we're actually connected before trying anything
		if (mBluetoothService.getState() != BluetoothService.STATE_CONNECTED) {
			Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT)
					.show();
			return;
		}

		if (snipe != null) {
			// Send the selected snipe
			byte[] send = snipe.getBytes();
			mBluetoothService.write(send);
		}
	}

	private void doBattle() {

		// Start battle simulation on server side
		if (server == 1) {

			boolean serverTurn = new Random().nextBoolean();
			int healthSelf = snipeSelf.getHealth();
			int healthOpponent = snipeOpponent.getHealth();

			// Combat process, snipes will attack in turn 
			while (healthSelf > 0 || healthOpponent > 0) {

				if (serverTurn) {
					int damage = snipeSelf.attack() - snipeOpponent.defend();
					healthOpponent -= damage >= 1 ? damage : 1;
				} else {
					int damage = snipeOpponent.attack() - snipeSelf.defend();
					healthSelf -= damage >= 1 ? damage : 1;

				}
				serverTurn = !serverTurn;
			}

			if (snipeSelf.getHealth() > 0) {
				Toast.makeText(getApplicationContext(), "You WIN!",
						Toast.LENGTH_LONG).show();
				mBluetoothService.write("win".getBytes());
			} else {
				Toast.makeText(getApplicationContext(), "You LOSE!",
						Toast.LENGTH_LONG).show();
				mBluetoothService.write("lose".getBytes());
			}
		}
	}

	// The Handler that gets information back from the BluetoothService
	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MESSAGE_STATE_CHANGE:
				if (D)
					Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
				switch (msg.arg1) {
				case BluetoothService.STATE_CONNECTED:
					snipeSelf = null;
					snipeOpponent = null;
					break;
				case BluetoothService.STATE_CONNECTING:
					break;
				case BluetoothService.STATE_LISTEN:
				case BluetoothService.STATE_NONE:
					break;
				}
				break;
			case MESSAGE_WRITE:
				byte[] writeBuf = (byte[]) msg.obj;
				// construct a string from the buffer
				String messageString = new String(writeBuf);

				if (messageString.equalsIgnoreCase("win")) {
					Toast.makeText(getApplicationContext(), "You WIN!", Toast.LENGTH_LONG).show();
					break;
				}
				if (messageString.equalsIgnoreCase("lose")) {
					Toast.makeText(getApplicationContext(), "You LOSE!", Toast.LENGTH_LONG).show();
					break;
				}

				snipeSelf = SnipeCollection.getSnipe(messageString);

				if (snipeOpponent != null && server != -1) {
					Toast.makeText(
							getApplicationContext(),
							(server == 1 ? "Server" : "Client") + " mode: "
									+ snipeSelf + " (me) vs " + snipeOpponent
									+ " (" + mConnectedDeviceName + ")",
							Toast.LENGTH_SHORT).show();
					doBattle();
				} else {
					if (server == -1)
						server = 1;
				}

				break;
			case MESSAGE_READ:
				byte[] readBuf = (byte[]) msg.obj;
				// construct a string from the valid bytes in the buffer
				String nameOpponent = new String(readBuf, 0, msg.arg1);
				snipeOpponent = SnipeCollection.getSnipe(nameOpponent);

				if (snipeSelf != null && server != -1) {
					Toast.makeText(
							getApplicationContext(),
							(server == 1 ? "Server" : "Client") + " mode: "
									+ snipeSelf + " (me) vs " + snipeOpponent
									+ " (" + mConnectedDeviceName + ")",
							Toast.LENGTH_SHORT).show();
					doBattle();
				} else {
					if (server == -1)
						server = 0;
				}

				break;
			case MESSAGE_DEVICE_NAME:
				// save the connected device's name
				mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
				Toast.makeText(getApplicationContext(),
						"Connected to " + mConnectedDeviceName,
						Toast.LENGTH_SHORT).show();
				break;
			case MESSAGE_TOAST:
				Toast.makeText(getApplicationContext(),
						msg.getData().getString(TOAST), Toast.LENGTH_SHORT)
						.show();
				break;
			}
		}
	};

	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (D)
			Log.d(TAG, "onActivityResult " + resultCode);
		switch (requestCode) {
		case REQUEST_CONNECT_DEVICE:
			// When DeviceListActivity returns with a device to connect
			if (resultCode == Activity.RESULT_OK) {
				connectDevice(data);
			}
			break;
		case REQUEST_ENABLE_BT:
			// When the request to enable Bluetooth returns
			if (resultCode == Activity.RESULT_OK) {
				// Bluetooth is now enabled, so set up a battle
				setupBattle();
			} else {
				// User did not enable Bluetooth or an error occured
				Log.d(TAG, "BT not enabled");
				Toast.makeText(this, R.string.bt_not_enabled_leaving,
						Toast.LENGTH_SHORT).show();
				finish();
			}
		}
	}

	private void connectDevice(Intent data) {
		// Get the device MAC address
		String address = data.getExtras().getString(
				DeviceListActivity.EXTRA_DEVICE_ADDRESS);
		// Get the BluetoothDevice object
		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		// Attempt to connect to the device
		mBluetoothService.connect(device);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.option_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Intent serverIntent = null;
		switch (item.getItemId()) {
		case R.id.connect_scan:
			// Launch the DeviceListActivity to see devices and do scan
			serverIntent = new Intent(this, DeviceListActivity.class);
			startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
			return true;
		case R.id.discoverable:
			// Ensure this device is discoverable by others
			ensureDiscoverable();
			return true;
		}
		return false;
	}

}
